package com.jfinal.ext.annotation;

import com.jfinal.config.*;
import com.jfinal.core.Controller;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Model;

import java.io.File;


public class AnnotationBindPlugin implements IPlugin {
    private Routes routes;
    private Handlers handlers;
    private Interceptors interceptors;
    private Constants constants;
    private Plugins plugins;
    private ActiveRecordPlugin arp;
    private JFinalConfig config;

    /**
     * @param config
     */
    public AnnotationBindPlugin(JFinalConfig config) {
        this.config = config;
    }

    public AnnotationBindPlugin set(Routes routes) {
        this.routes = routes;
        return this;
    }

    public AnnotationBindPlugin set(Handlers handlers) {
        this.handlers = handlers;
        return this;
    }

    public AnnotationBindPlugin set(Interceptors interceptors) {
        this.interceptors = interceptors;
        return this;
    }

    public AnnotationBindPlugin set(Constants constants) {
        this.constants = constants;
        return this;
    }

    public AnnotationBindPlugin set(Plugins plugins) {
        this.plugins = plugins;
        return this;
    }

    public AnnotationBindPlugin set(ActiveRecordPlugin arp) {
        this.arp = arp;
        return this;
    }

    /**
     * @param file
     * @param pkg
     * @return
     */
    private Object bindPackage(File file, String pkg) {
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                String thisPkg;
                if (pkg.equals("") || (null == pkg)) {
                    thisPkg = f.getName();
                } else {
                    thisPkg = pkg + "." + f.getName();
                }
                bindPackage(f, thisPkg);
            }
            return false;
        }
        if(!pkg.contains(".class")) return null;
        try {
            Class clazz = Class.forName(pkg.replace(".class", ""));
            if (Controller.class.isAssignableFrom(clazz)) bindRoute((Class<? extends Controller>) clazz);
            if (Model.class.isAssignableFrom(clazz)) bindModel((Class<? extends Model<?>>) clazz);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param clazz
     * @return
     */
    private Object bindRoute(Class<? extends Controller> clazz) {
        if (this.routes == null || clazz.getAnnotation(IgnoreRoute.class) != null) return null;

        RouteBind routeBind = clazz.getAnnotation(RouteBind.class);
        String className = clazz.getName();
        String route = className.substring(className.lastIndexOf(".") + 1).replaceAll("Controller", "");
        route = "/" + route.substring(0,1).toLowerCase() + route.substring(1);

        if (routeBind != null) {
            if (!routeBind.value().equals("")) route = routeBind.value();
        }

        this.routes.add(route, clazz);
        System.out.println("Bind:Controller -> " + route + "\t" + clazz.getName());
        return null;
    }

    /**
     * @param clazz
     * @return
     */
    private Object bindModel(Class<? extends Model<?>> clazz) {
        if (this.arp == null || clazz.getAnnotation(IgnoreModel.class) != null) return null;

        TableBind tableBind = clazz.getAnnotation(TableBind.class);
        String className = clazz.getName();
        String table = className.substring(className.lastIndexOf(".") + 1).toLowerCase();
        String tableId = "id";

        if (tableBind != null) {
            if (!tableBind.value().equals("")) table = tableBind.value();
            tableId = tableBind.primaryKey();
        }

        this.arp.addMapping(table, tableId, clazz);
        System.out.println("Bind:Model -> " + table + "\t" + tableId + "\t" + clazz.getName());
        return null;
    }

    /**
     * @return
     */
    private Object bindClass() {
        String classesPath = config.getClass().getClassLoader().getResource(".").getPath();
        bindPackage(new File(classesPath), "");
        return null;
    }

    public boolean start() {
        bindClass();
        return true;
    }

    public boolean stop() {
        return true;
    }

}